#include <assert.h>

#include "../src/multiparse.h"

static char*
random_worker_string (void)
{
  unsigned int len = 1.0 + 10.0 * drand48 ();
  char buf[1 + len];

  for (unsigned int n = 0; n < len; ++n)
    {
      do
        {
          buf[n] = lrand48 ();
        }
      while (! isgraph (buf[n]) || buf[n] == ':');
    }
  buf[len] = '\0';

  return strdup (buf);
}

static void
random_nonpipe (char*        tag,
                unsigned int len)
{
  for (unsigned int n = 0; n < len; ++n)
    {
      do
        {
          tag[n] = lrand48 () % 256;
        }
      while (! isgraph (tag[n]) || tag[n] == '|');
    }
}

static const char*
to_string (unsigned int label,
           unsigned int n_raw_labels,
           unsigned int max_raw_labels,
           bool         dash_only)
{
  if (label == 0)
    {
      return (dash_only || drand48 () < 0.5) ? "-" : "";
    }
  else
    {
      static char buf[1024];
      char* p = buf;
      unsigned int labels[max_raw_labels];
      unsigned int depth = index_to_set (label, labels, n_raw_labels);

      assert (depth <= max_raw_labels);

      *p = '\0';
      for (unsigned int n = 0; n < depth; ++n)
        {
          int n_char = snprintf (p,
                                 sizeof (buf) - (p - buf),
                                 "%s%d", (n == 0) ? "" : ",",
                                 labels[n]);

          p += n_char;
        }

      return buf;
    }
}

static void
print_file (FILE* to,
            FILE* f)
{
  char buf[1024];

  fseek (f, SEEK_SET, 0);

  while (fgets (buf, sizeof (buf), f) != NULL)
    {
      fprintf (to, "%s", buf);
    }
}

static void
test_parse_once (void)
{
  FILE* f = tmpfile ();
  unsigned int n_raw_labels = 1.0 + 10.0 * drand48 ();
  unsigned int max_raw_labels = n_raw_labels * drand48 ();
  unsigned int effective_n_labels = 0;

  for (unsigned int m = 0; m <= max_raw_labels; ++m)
    {
      effective_n_labels += binomial (n_raw_labels, m);
    }

  unsigned int n_workers = 1.0 + 50.0 * drand48 ();
  unsigned int n_ratings = 1.0 + 50.0 * drand48 ();
  int clamp = (drand48 () < 0.5) ? -1 : effective_n_labels * drand48 ();
  Rating ratings[n_ratings];
  unsigned int n_tag = 0.0 + 50.0 * drand48 ();
  char tag[n_tag + 1];

  random_nonpipe (tag, n_tag);
  tag[n_tag] = '\0';

  if (clamp >= 0)
    {
      fprintf (f, 
               "%s %s|", 
               to_string (clamp, n_raw_labels, max_raw_labels, true), 
               tag);
    }
  else
    {
      fprintf (f, " %s|", tag);
    }

  for (unsigned int n = 0; n < n_ratings; ++n)
    {
      unsigned int label = effective_n_labels * drand48 ();
      ratings[n].rating = label;

      if (drand48 () < 0.5)
        {
          unsigned int worker = lrand48 ();
          ratings[n].worker = worker % n_workers;

          fprintf (f,
                   " %u:%s",
                   worker,
                   to_string (label, n_raw_labels, max_raw_labels, false));
        }
      else
        {
          char* worker = random_worker_string ();
          ratings[n].worker = hash (worker) % n_workers;

          fprintf (f, 
                   " %s:%s", 
                   worker, 
                   to_string (label, n_raw_labels, max_raw_labels, false));

          free (worker);
        }
    }

  fprintf (f, "\n");

  fseek (f, SEEK_SET, 0);

  Rating the_ratings[n_ratings];
  char the_tag[1024];
  int the_clamp = -1;
  int rv = parse_multi_ratings (f,
                                the_ratings,
                                n_ratings + 3.0 * drand48 (),
                                the_tag,
                                sizeof (the_tag),
                                &the_clamp,
                                69,
                                n_raw_labels,
                                max_raw_labels,
                                n_workers);

  assert (rv == (int) n_ratings || (fprintf (stderr, "(n,max)=(%u,%u) rv = %d ?= %u = n_ratings\n", n_raw_labels, max_raw_labels, rv, n_ratings), print_file (stderr, f), 0));
  assert (strcmp (tag, the_tag) == 0 || (fprintf (stderr, "(n,max)=(%u,%u) tag='%s' ?= '%s' = the_tag\n", n_raw_labels, max_raw_labels, tag, the_tag), print_file (stderr, f), 0));
  assert (memcmp (ratings, the_ratings, n_ratings * sizeof (ratings[0])) == 0 ||
          (print_file (stderr, f), 0));
  assert (clamp == the_clamp || 
          (fprintf (stderr, "(n,max)=(%u,%u), clamp = %d ?= %d = the_clamp\n", n_raw_labels, max_raw_labels, clamp, the_clamp), print_file (stderr, f), 0));

  fclose (f);
}

static void
test_parse (void)
{
  for (unsigned int i = 0; i < 1000; ++i)
    {
      test_parse_once ();
    }
}

int 
main (void) 
{
  srand48 (69);

  (void) parse_ratings;

  test_parse ();

  return 0;
}
